% Chakram is a subproject of Hexena - Haskell Executable Analyser
% License:			GPL-3
% Maintainer:		Montez Fitzpatrick
% Stability:		Experimental
% Portability:		GHC
% Module:			Hexena.Codec.Parser.OptionalHeader
\begin{code}
module Hexena.Codec.Parser.OptionalHeader
	(
	-- * Parsing
	  parseOptHeader
	) where

import Data.Binary.Get
import Data.Word
import Control.Monad (replicateM)

import Hexena.Data.OptionalHeader (OptionalHeader(..))
import Hexena.Data.OptionalHeader.X86 (X86(..))
import Hexena.Data.OptionalHeader.AMD64 (AMD64(..))
-- ort Hexena.Data.OptionalHeader.ROM (ROM(..))
import Hexena.Data.OptionalHeader.DataDirectory (DataDirectory(..), DataEntry(..))

import Hexena.Internal.Constants

parseOptHeader :: Get OptionalHeader
parseOptHeader = do
  oh_magic <- getWord16le
   -- Checking to see if the magic is 32 or 64
  case oh_magic of
	iMAGE_NT_OPTIONAL_HDR32_MAGIC -> parseOptHdr32 oh_magic
	iMAGE_NT_OPTIONAL_HDR64_MAGIC -> parseOptHdr64 oh_magic
	iMAGE_ROM_OPTIONAL_HDR_MAGIC  -> parseOptHdrRom oh_magic
	otherwise					  -> error "Unrecognized Magic File Header"

parseOptHdr32 :: Word16 -> Get OptionalHeader
parseOptHdr32 magic = do 
  oh_majLdVers <- getWord8
  oh_mnrLdVers <- getWord8
  oh_szOfCode <- getWord32le
  oh_szOfInitData <- getWord32le
  oh_szOfUnInitData <- getWord32le
  oh_addrEntryPnt <- getWord32le
  oh_baseOfCode <- getWord32le
  oh_baseOfData <- getWord32le
  oh_imageBase <- getWord32le
  oh_secAlign <- getWord32le
  oh_fileAlign <- getWord32le
  oh_majOSVers <- getWord16le
  oh_mnrOSVers <- getWord16le
  oh_majImgVers <- getWord16le
  oh_mnrImgVers <- getWord16le
  oh_majSubsysVers <- getWord16le
  oh_mnrSubsysVers <- getWord16le
  oh_w32VersVal <- getWord32le
  oh_szOfImage <- getWord32le
  oh_szOfHdrs <- getWord32le
  oh_chkSum    <- getWord32le
  oh_subsys <- getWord16le
  oh_dllChar <- getWord16le
  oh_szOfStkRes <- getWord32le
  oh_szOfStkCmt <- getWord32le
  oh_szOfHpRes <- getWord32le
  oh_szOfHpCmt<- getWord32le
  oh_ldrFlags <- getWord32le
  oh_numRvaSz <- getWord32le
  oh_dataDir <- parseDataDir oh_numRvaSz

  return $ X86 magic oh_majLdVers oh_mnrLdVers oh_szOfCode oh_szOfInitData oh_szOfUnInitData oh_addrEntryPnt oh_baseOfCode oh_baseOfData oh_imageBase oh_secAlign oh_fileAlign oh_majOSVers oh_mnrOSVers oh_majImgVers oh_mnrImgVers oh_majSubsysVers oh_mnrSubsysVers oh_w32VersVal oh_szOfImage oh_szOfHdrs oh_chkSum oh_subsys oh_dllChar oh_szOfStkRes oh_szOfStkCmt oh_szOfHpRes oh_szOfHpCmt oh_ldrFlags oh_numRvaSz oh_dataDir
  
parseOptHdr64 :: Word16 -> Get OptionalHeader
parseOptHdr64 magic = do
  oh_majLdVers <- getWord8
  oh_mnrLdVers <- getWord8
  oh_szOfCode <- getWord32le
  oh_szOfInitData <- getWord32le
  oh_szOfUnInitData <- getWord32le
  oh_addrEntryPnt <- getWord32le
  oh_baseOfCode <- getWord32le
  oh_imageBase <- getWord64le
  oh_secAlign <- getWord32le
  oh_fileAlign <- getWord32le
  oh_majOSVers <- getWord16le
  oh_mnrOSVers <- getWord16le
  oh_majImgVers <- getWord16le
  oh_mnrImgVers <- getWord16le
  oh_majSubsysVers <- getWord16le
  oh_mnrSubsysVers <- getWord16le
  oh_w32VersVal <- getWord32le
  oh_szOfImage <- getWord32le
  oh_szOfHdrs <- getWord32le
  oh_chkSum    <- getWord32le
  oh_subsys <- getWord16le
  oh_dllChar <- getWord16le
  oh_szOfStkRes <- getWord64le
  oh_szOfStkCmt<- getWord64le
  oh_szOfHpRes <- getWord64le
  oh_szOfHpCmt<- getWord64le
  oh_ldrFlags <- getWord32le
  oh_numRvaSz <- getWord32le
  oh_dataDir <- parseDataDir oh_numRvaSz

  return $ AMD64 magic oh_majLdVers oh_mnrLdVers oh_szOfCode oh_szOfInitData oh_szOfUnInitData oh_addrEntryPnt oh_baseOfCode oh_imageBase oh_secAlign oh_fileAlign oh_majOSVers oh_mnrOSVers oh_majImgVers oh_mnrImgVers oh_majSubsysVers oh_mnrSubsysVers oh_w32VersVal oh_szOfImage oh_szOfHdrs oh_chkSum oh_subsys oh_dllChar oh_szOfStkRes oh_szOfStkCmt oh_szOfHpRes oh_szOfHpCmt oh_ldrFlags oh_numRvaSz oh_dataDir

-- parseOptHdrRom :: Word16 -> Get OptionalHeader
parseOptHdrRom magic = undefined

-- | This is an overall container for individual Data Directory Entries
--   It does however, calculate how many entries will be required.
parseDataDir :: Word32 -> Get DataDirectory
parseDataDir n = do
  -- | PE Files have a maximum of 16 sections in the DataDir
  --   most windows tools just set this to 16 by default
  --   instead of hard-coding 16 sections we will verify
  let nDirs = fromIntegral (min mAX_NUMBEROF_DIRECTORY_ENTRIES n) :: Int
  dd_dentries <- parseDataEntry nDirs

  return $ DataDirectory dd_dentries

parseDataEntry :: Int -> Get [DataEntry]
parseDataEntry n = 
  replicateM n $ getWord32le >>= \vAddr -> getWord32le >>= \ddSize -> return $ DataEntry vAddr ddSize

\end{code}
